
package com.fl.util;

import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
 * 字符串工具类<br>
 * <li>字符串左右补全指定字符或者字符串</li> <br>
 * <li>字符串转换</li> <br>
 * <li>兼容null字符串的trim</li> <br>
 * <li>判断是否为空字符（包括null）</li> <br>
 * <li>是否整数格式</li> <br>
 * <li>是否浮点数格式</li> <br>
 * <li>是否Email格式</li> <br>
 * <li>十六进制字符串和二进制字符串之间互相转换</li><br>
 * <li>字符串前补长度位</li><br>
 * <li>根据指定字符集名称获取字符串所占字节数</li><br>
 * <li>卡号中间用*字符混淆</li><br>
 * 
 * */
public class StringUtil {

    /** 空字符串 */
    public static final String  EMPTY                = "";

    /** 补全最大字符串长度 */
    private static final int    PAD_LIMIT            = 8192;

    /*-----------------------------------正则表达式-------------------------------*/
    private static final String EMAIL_PATTERN        = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
    private static Pattern      emailPattern;

    private static final String INTEGER_PATTERN      = "^-?[1-9]\\d*$";
    private static Pattern      integerPattern;

    private static final String FLOAT_PATTERN        = "^(-?\\d+)(\\.\\d+)?$";
    private static Pattern      floatPattern;

    private static final String UNSIGNED_HEX_PATTERN = "^[0-9A-Fa-f]*$";
    private static Pattern      unsignedHexPattern;

    private static final String UNSIGNED_BIN_PATTERN = "^[0,1]*$";
    private static Pattern      unsignedBinPattern;

    private static final String CARD_NO_PATTERN      = "^[0-9]{16,19}$";
    private static Pattern      cardNoPattern;

    static {
        emailPattern = Pattern.compile(EMAIL_PATTERN);
        integerPattern = Pattern.compile(INTEGER_PATTERN);
        floatPattern = Pattern.compile(FLOAT_PATTERN);
        unsignedHexPattern = Pattern.compile(UNSIGNED_HEX_PATTERN);
        unsignedBinPattern = Pattern.compile(UNSIGNED_BIN_PATTERN);
        cardNoPattern = Pattern.compile(CARD_NO_PATTERN);
    }

    /**
     * 判断字符串是否为空字符串("") 或者 null <br>
     * 
     * <pre>
     * StringUtil.isEmpty(null)      = true
     * StringUtil.isEmpty("")        = true
     * StringUtil.isEmpty(" ")       = false
     * StringUtil.isEmpty("up")      = false
     * StringUtil.isEmpty("  up  ")  = false
     * </pre>
     * 
     * @param cs
     *            待校验的字符串，可为null
     * @return {@code true} 字符串为空串或者null
     */
    public static boolean isEmpty(CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    /**
     * 判断字符串是否为空字符串("") 或者 null, 判断之前trim字符串 <br>
     * 
     * <pre>
     * StringUtil.isBlank(null)      = true
     * StringUtil.isBlank("")        = true
     * StringUtil.isBlank(" ")       = true
     * StringUtil.isBlank("up")      = false
     * StringUtil.isBlank("  up  ")  = false
     * </pre>
     * 
     * @param cs
     *            待校验的字符串，可为null
     * @return {@code true} 字符串为空串、null或者连续空格
     */
    public static boolean isBlank(String cs) {
        return cs == null || cs.trim().length() == 0;
    }

    /**
     * 判断字符串是否为整数格式 <br>
     * 
     * <pre>
     * StringUtil.isInt(null)      = false
     * StringUtil.isInt("")        = false
     * StringUtil.isInt("11123")   = true
     * StringUtil.isInt("-9")      = true
     * StringUtil.isInt("+1")      = false
     * StringUtil.isInt("09")      = false
     * StringUtil.isInt("1.1")     = false
     * StringUtil.isInt(" 2")      = false
     * </pre>
     * 
     * @param str
     *            待校验的字符串，可为null
     */
    public static boolean isInt(String str) {
        if (isEmpty(str)) {
            return false;
        }

        Matcher matcher = integerPattern.matcher(str);
        return matcher.matches();
    }

    /**
     * 判断字符串是否为浮点数格式 <br>
     * 
     * <pre>
     * StringUtil.isFloat(null)      = false
     * StringUtil.isFloat("")        = false
     * StringUtil.isFloat("0.121")   = true
     * StringUtil.isFloat("-15.42")  = true
     * StringUtil.isFloat(".3")      = false
     * StringUtil.isFloat("+1.3")    = false
     * StringUtil.isFloat("1.2E10")  = false
     * </pre>
     * 
     * @param str
     *            待校验的字符串，可为null
     */
    public static boolean isFloat(String str) {
        if (isEmpty(str)) {
            return false;
        }

        Matcher matcher = floatPattern.matcher(str);
        return matcher.matches();
    }

    /***
     * 兼容null的trim<br>
     * 
     * <pre>
     * StringUtil.trim(null )        = ""
     * StringUtil.trim("" )          = ""
     * StringUtil.trim("up  " )      = "up"
     * StringUtil.trim("   up" )     = "up"
     * StringUtil.trim(" up  " )     = "up"
     * StringUtil.trim("u p" )       = "u p"
     * </pre>
     * 
     * @param str
     *            需要trim的字符串，可为null
     * @return 空字符串或者调用String.trim()掉的字符串
     * */
    public static String trim(String str) {
        if (isEmpty(str)) {
            return EMPTY;
        }

        return str.trim();
    }

    /**
     * 判断字符串是否为Email格式 <br>
     * 
     * <pre>
     * StringUtil.isEmail(null)                         = false
     * StringUtil.isEmail("")                           = false
     * StringUtil.isEmail(" ")                          = false
     * StringUtil.isEmail("abc@abc.com")                = true
     * StringUtil.isEmail("abc@aaaa.m")                 = false
     * StringUtil.isEmail("steve.jobs@apple.com")       = true
     * StringUtil.isEmail("1111abc@abc.com")            = true
     * StringUtil.isEmail("abc@abc.com.cn")             = true
     * StringUtil.isEmail("abc@abc.com.")               = false
     * StringUtil.isEmail("  abc@abc.com  ")            = false
     * StringUtil.isEmail("a_b_c@abc.com")              = true
     * StringUtil.isEmail("abc@ab_c.com")               = false
     * </pre>
     * 
     * @param str
     *            待校验的字符串，可为null
     * @return {@code true} 字符串为空串或者null
     */
    public static boolean isEmail(String str) {
        if (isEmpty(str)) {
            return false;
        }

        Matcher matcher = emailPattern.matcher(str);
        return matcher.matches();
    }

    /**
     * <p>
     * 重复输出制定长度的字符组成字符串<br>
     * </p>
     * 
     * <pre>
     * StringUtil.repeat('e', 0)   = ""
     * StringUtil.repeat('e', 3)   = "eee"
     * StringUtil.repeat('e', -2)  = ""
     * </pre>
     * 
     * 
     * @param ch
     *            需要重复的字符
     * @param repeat
     *            重复次数
     * @return 重复输出的字符
     */
    private static String repeat(char ch, int repeat) {
        char[] buf = new char[repeat];
        for (int i = repeat - 1; i >= 0; i--) {
            buf[i] = ch;
        }
        return new String(buf);
    }

    /**
     * <p>
     * 字符串右补空格，补全到长度{@code size}
     * </p>
     * 
     * <pre>
     * StringUtil.rightPad(null, *)   = null
     * StringUtil.rightPad("", 3)     = "   "
     * StringUtil.rightPad("bat", 3)  = "bat"
     * StringUtil.rightPad("bat", 5)  = "bat  "
     * StringUtil.rightPad("bat", 1)  throws IllegalArgumentException
     * StringUtil.rightPad("bat", -1) = "bat"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全长度
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String rightPad(String str, int size) {
        return rightPad(str, size, ' ');
    }

    /**
     * <p>
     * 字符串右补单个字符，补全到长度{@code size}, 补全的单个字符为{@code padChar}
     * </p>
     * 
     * <pre>
     * StringUtil.rightPad(null, *, *)     = null
     * StringUtil.rightPad("", 3, 'z')     = "zzz"
     * StringUtil.rightPad("bat", 3, 'z')  = "bat"
     * StringUtil.rightPad("bat", 5, 'z')  = "batzz"
     * StringUtil.rightPad("bat", 1, 'z')  throws IllegalArgumentException
     * StringUtil.rightPad("bat", -1, 'z') = "bat"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全到的长度
     * @param padChar
     *            待补全的单个字符
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String rightPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        }

        int pads = size - str.length();

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }
        if (pads > PAD_LIMIT) {
            return rightPad(str, size, String.valueOf(padChar));
        }
        return str.concat(repeat(padChar, pads));
    }

    /**
     * <p>
     * 字符串右补字符串，补全到长度{@code size}, 补全的字符串为{@code padStr}
     * </p>
     * 
     * <pre>
     * StringUtil.rightPad(null, *, *)      = null
     * StringUtil.rightPad("", 3, "z")      = "zzz"
     * StringUtil.rightPad("bat", 3, "yz")  = "bat"
     * StringUtil.rightPad("bat", 5, "yz")  = "batyz"
     * StringUtil.rightPad("bat", 8, "yz")  = "batyzyzy"
     * StringUtil.rightPad("bat", 1, "yz")  throws IllegalArgumentException
     * StringUtil.rightPad("bat", 5, "abcd")= "batab" 
     * StringUtil.rightPad("bat", -1, "yz") = "bat"
     * StringUtil.rightPad("bat", 5, null)  = "bat  "
     * StringUtil.rightPad("bat", 5, "")    = "bat  "
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全到的长度
     * @param padStr
     *            待补全的字符串
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String rightPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        }
        if (isEmpty(padStr)) {
            padStr = " ";
        }
        int padLen = padStr.length();
        int strLen = str.length();
        int pads = size - strLen;

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }
        if (padLen == 1 && pads <= PAD_LIMIT) {
            return rightPad(str, size, padStr.charAt(0));
        }

        if (pads == padLen) {
            return str.concat(padStr);
        } else if (pads < padLen) {
            return str.concat(padStr.substring(0, pads));
        } else {
            char[] padding = new char[pads];
            char[] padChars = padStr.toCharArray();
            for (int i = 0; i < pads; i++) {
                padding[i] = padChars[i % padLen];
            }
            return str.concat(new String(padding));
        }
    }

    /**
     * <p>
     * 字符串右补字符，补全到{@code size}个字节, 补全的字符为{@code padChar},字符串{@code charSetName}字符集计算长度
     * </p>
     * 
     * <pre>
     * StringUtil.rightPadByCharset(null,        10,'\0',"GBK")            throws IllegalArgumentException
     * StringUtil.rightPadByCharset("这是我的字符串",  6,'\0',"GBK")             throws IllegalArgumentException
     * StringUtil.rightPadByCharset("中国", 10, '\0', "不支持的charSet")       throws UnsupportedEncodingException
     * StringUtil.rightPadByCharset("",3,'\0',"GBK")                       = "\0\0\0"
     * StringUtil.rightPadByCharset("字符信息", 12, 'A', "UTF-8")             = "字符信息"
     * StringUtil.rightPadByCharset("字符信息", 12, 'A', "GBK")               = "字符信息AAAA"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全到的长度
     * @param padChar
     *            待补全的字符
     * @param charSetName
     *            字符集名称
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String rightPadByCharset(String str, int size, char padChar, String charSetName)
            throws UnsupportedEncodingException {
        if (str == null) {
            return null;
        }

        // 根据charSetName字符集算出字符串str已经占用多少个字节
        int oldSize = StringUtil.byteCountWithCharSet(str, charSetName);

        int pads = size - oldSize;

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size + " while str already has a size of " + oldSize);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }

        return str.concat(repeat(padChar, pads));
    }

    /**
     * <p>
     * 字符串左补空格，补全到长度{@code size}
     * </p>
     * 
     * <pre>
     * StringUtil.leftPad(null, *)   = null
     * StringUtil.leftPad("", 3)     = "   "
     * StringUtil.leftPad("bat", 3)  = "bat"
     * StringUtil.leftPad("bat", 5)  = "  bat"
     * StringUtil.leftPad("bat", 1)  throws IllegalArgumentException
     * StringUtil.leftPad("bat", -1) = "bat"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全长度
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String leftPad(String str, int size) {
        return leftPad(str, size, ' ');
    }

    /**
     * <p>
     * 字符串左补单个字符，补全到长度{@code size}, 补全的单个字符为{@code padChar}
     * </p>
     * 
     * <pre>
     * StringUtil.leftPad(null, *, *)     = null
     * StringUtil.leftPad("", 3, 'z')     = "zzz"
     * StringUtil.leftPad("bat", 3, 'z')  = "bat"
     * StringUtil.leftPad("bat", 5, 'z')  = "zzbat"
     * StringUtil.leftPad("bat", 1, 'z')  throws IllegalArgumentException
     * StringUtil.leftPad("bat", -1, 'z') = "bat"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全长度
     * @param padChar
     *            待补全的单个字符
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String leftPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        }
        int pads = size - str.length();

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }

        if (pads > PAD_LIMIT) {
            return leftPad(str, size, String.valueOf(padChar));
        }
        return repeat(padChar, pads).concat(str);
    }

    /**
     * <p>
     * 字符串左补字符串，补全到长度{@code size}, 补全的字符串为{@code padStr}
     * </p>
     * 
     * <pre>
     * StringUtil.leftPad(null, *, *)      = null
     * StringUtil.leftPad("", 3, "z")      = "zzz"
     * StringUtil.leftPad("bat", 3, "yz")  = "bat"
     * StringUtil.leftPad("bat", 5, "yz")  = "yzbat"
     * StringUtil.leftPad("bat", 8, "yz")  = "yzyzybat"
     * StringUtil.leftPad("bat", 1, "yz")  throws IllegalArgumentException
     * StringUtil.leftPad("bat", 5, "abcd")= "abbat"
     * StringUtil.leftPad("bat", -1, "yz") = "bat"
     * StringUtil.leftPad("bat", 5, null)  = "  bat"
     * StringUtil.leftPad("bat", 5, "")    = "  bat"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全长度
     * @param padStr
     *            待补全的字符串
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String leftPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        }
        if (isEmpty(padStr)) {
            padStr = " ";
        }
        int padLen = padStr.length();
        int strLen = str.length();
        int pads = size - strLen;

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }

        if (padLen == 1 && pads <= PAD_LIMIT) {
            return leftPad(str, size, padStr.charAt(0));
        }

        if (pads == padLen) {
            return padStr.concat(str);
        } else if (pads < padLen) {
            return padStr.substring(0, pads).concat(str);
        } else {
            char[] padding = new char[pads];
            char[] padChars = padStr.toCharArray();
            for (int i = 0; i < pads; i++) {
                padding[i] = padChars[i % padLen];
            }
            return new String(padding).concat(str);
        }
    }

    /**
     * <p>
     * 字符串左补字符，补全到{@code size}个字节, 补全的字符为{@code padChar},字符串{@code charSetName}字符集计算长度
     * </p>
     * 
     * <pre>
     * StringUtil.leftPadByCharset(null,        10,'\0',"GBK")            throws IllegalArgumentException
     * StringUtil.leftPadByCharset("这是我的字符串",  6,'\0',"GBK")             throws IllegalArgumentException
     * StringUtil.leftPadByCharset("中国", 10, '\0', "不支持的charSet")       throws UnsupportedEncodingException
     * StringUtil.leftPadByCharset("",3,'\0',"GBK")                       = "\0\0\0"
     * StringUtil.leftPadByCharset("字符信息", 12, 'A', "UTF-8")             = "字符信息"
     * StringUtil.leftPadByCharset("字符信息", 12, 'A', "GBK")               = "AAAA字符信息"
     * </pre>
     * 
     * @param str
     *            待补全的字符串，可为空或null
     * @param size
     *            补全到的长度
     * @param padChar
     *            待补全的字符
     * @param charSetName
     *            字符集名称
     * @return 补全的字符串、原长度超过不需补全时则返回原字符串， null则返回{@code null}
     */
    public static String leftPadByCharset(String str, int size, char padChar, String charSetName)
            throws UnsupportedEncodingException {
        if (str == null) {
            return null;
        }

        // 根据charSetName字符集算出字符串str已经占用多少个字节
        int oldSize = StringUtil.byteCountWithCharSet(str, charSetName);
        int pads = size - oldSize;

        if (pads < 0) {
            throw new IllegalArgumentException("wrong size " + size + " while str already has a size of " + oldSize);
        }

        if (pads == 0) {
            // 长度满足，直接返回
            return str;
        }

        return repeat(padChar, pads).concat(str);
    }

    /**
     * 十六进制的字符串，转换为二进制的字符串<br>
     * 
     * <pre>
     * StringUtil.hexToBit(null)   throws NumberFormatException
     * StringUtil.hexToBit("-1")   throws NumberFormatException
     * StringUtil.hexToBit("0x11") throws NumberFormatException
     * StringUtil.hexToBit("")     = ""  
     * StringUtil.hexToBit("FF")   = "11111111"
     * StringUtil.hexToBit("F")    = "1111"
     * StringUtil.hexToBit("1")    = "0001"
     * StringUtil.hexToBit("3")    = "0011"
     * StringUtil.hexToBit("8")    = "1000"
     * StringUtil.hexToBit("F1")   = "11110001"
     * StringUtil.hexToBit("05")   = "00000101"
     * StringUtil.hexToBit("601")  = "011000000001"
     * </pre>
     * 
     * @param hexStr 十六进制字符串
     *            ，不能带符号
     * @throws NumberFormatException
     *             null,或者字符串中包含非0-9a-fA-F的字符
     * @return 二进制字符串，高位补0以满足一个十六进制字符对应4个二进制字符的对应关系
     * */
    public static String hexToBit(String hexStr) throws NumberFormatException {
        // 判null
        if (hexStr == null) {
            throw new NumberFormatException("null");
        }

        // 判空
        if (hexStr == EMPTY) {
            return EMPTY;
        }

        // 判断格式，不支持符号
        Matcher matcher = unsignedHexPattern.matcher(hexStr);
        if (!matcher.matches()) {
            throw new NumberFormatException("十六进制字符格式不符，仅支持无符号十六进制字符");
        }

        StringBuilder sb = new StringBuilder(EMPTY);

        int len = hexStr.length();
        String subStr = hexStr;
        String leftStr = hexStr;

        do {
            // 每次拿7位字符出来转int，保证不溢出,Integer.MAX_VALUE = 0x7fffffff;
            if (len > 7) {

                len -= 7;
                subStr = leftStr.substring(0, 7);
                leftStr = leftStr.substring(7);

            }

            // 转int然后转bit字符串
            int value = Integer.parseInt(subStr, 16);

            // 高位补0以满足一个十六进制字符对应4个二进制字符的对应关系
            int size = subStr.length() * 4;
            String padded = StringUtil.leftPad(Integer.toBinaryString(value), size, '0');

            // 从高位往低位的append
            sb.append(padded);
        } while (len > 7);

        return sb.toString();
    }

    /**
     * 二进制的字符串，转换为十六制的字符串<br>
     * 
     * <pre>
     * StringUtil.bitToHex(null)             throws NumberFormatException
     * StringUtil.bitToHex("22A2")           throws NumberFormatException
     * StringUtil.bitToHex("-11001")         throws NumberFormatException
     * StringUtil.bitToHex("")               = ""
     * StringUtil.bitToHex("1")              = "1"
     * StringUtil.bitToHex("1111")           = "F" 
     * StringUtil.bitToHex("11110000")       = "F0"
     * StringUtil.bitToHex("11110000")       = "F0"
     * StringUtil.bitToHex("10000")          = "10"
     * StringUtil.bitToHex("10")             = "2"
     * StringUtil.bitToHex("0010")           = "2"
     * </pre>
     * 
     * @param  bitStr 二进制字符串
     *            ，不能带符号
     * @throws NumberFormatException
     *             null,或者字符串中包含非0或1的字符
     * @return 十六进制字符串,字母字符大写
     * */
    public static String bitToHex(String bitStr) throws NumberFormatException {
        // 判null
        if (bitStr == null) {
            throw new NumberFormatException("null");
        }

        // 判空
        if (bitStr == EMPTY) {
            return EMPTY;
        }

        // 判断格式，不支持正负号
        Matcher matcher = unsignedBinPattern.matcher(bitStr);
        if (!matcher.matches()) {
            throw new NumberFormatException("二进制字符格式不符，仅支持无符号二进制字符");
        }

        StringBuilder sb = new StringBuilder(EMPTY);

        int len = bitStr.length();
        String subStr = bitStr;
        String leftStr = bitStr;

        do {
            // 每次拿28位字符出来转int，保证不溢出, Integer.MAX_VALUE = 0x7fffffff = 2^31-1;
            if (len > 28) {
                len -= 28;
                subStr = leftStr.substring(len);
                leftStr = leftStr.substring(0, len);
            }

            // 转int然后转hex字符串
            int value = Integer.parseInt(subStr, 2);

            // 从低位往高位的insert
            sb.insert(0, Integer.toHexString(value));
        } while (len > 28);

        return sb.toString().toUpperCase();
    }

    /**
     * 在给定字符串前面增加表示字符串长度的前缀，前缀长度位数不足是左补0<br>
     * 
     * <pre>
     * StringUtil.lenPrefix(null,           2)              throws IllegalArgumentException 
     * StringUtil.lenPrefix("hhh",          0)              throws IllegalArgumentException 
     * StringUtil.lenPrefix("888",          4)              = "0003888"
     * StringUtil.lenPrefix("012345678910", 1)              throws IllegalArgumentException
     * </pre>
     * 
     * @param source
     *            源字符串
     * @param lenChars
     *            长度位字符串的长度,即用来填充前缀的字符个数，如0800010000，长度位的长度为2(个字符)
     * @return 增加了长度前缀的字符串
     * */
    public static String lenPrefix(String source, int lenChars) throws IllegalArgumentException {
        return lenPrefix(source, lenChars, '0');
    }

    /**
     * 在给定字符串前面增加表示字符串长度的前缀<br>
     * 
     * <pre>
     * StringUtil.lenPrefix(null,               2, ' ')              throws IllegalArgumentException 
     * StringUtil.lenPrefix("hhh",              0, ' ')              throws IllegalArgumentException 
     * StringUtil.lenPrefix("888",              2, '0')              = "03888"
     * StringUtil.lenPrefix("012345678",        3, ' ')              = "  9012345678"
     * StringUtil.lenPrefix("012345678910",     1, '0')              throws IllegalArgumentException
     * </pre>
     * 
     * @param source
     *            源字符串
     * @param lenChars
     *            长度位字符串的长度,即用来填充前缀的字符个数，如0800010000，长度位的长度为2(个字符)
     * @param prefixChar
     *            前缀字符串中补全到lenChars的字符
     * @return 增加了长度前缀的字符串
     * */
    public static String lenPrefix(String source, int lenChars, char prefixChar) throws IllegalArgumentException {
        if (source == null) {
            throw new IllegalArgumentException("null source string");
        }

        if (isEmpty(source.trim())) {
            return EMPTY;
        }

        if (lenChars <= 0) {
            throw new IllegalArgumentException("lenChars illegal:" + lenChars);
        }

        int len = source.trim().length();
        String lenStr = EMPTY + len;
        if (lenStr.length() <= lenChars) {
            // 长度位的长度够
            String prefixStr = StringUtil.leftPad(lenStr, lenChars, prefixChar);
            return prefixStr.concat(source.trim());
        } else {
            // 长度位参数不够
            throw new IllegalArgumentException("lenChars not enough for source string:" + source + " lenChars:"
                    + lenChars);
        }
    }

    /**
     * 按指定的字符编码来判定字符串占用几个字节<br>
     * 
     * <pre>
     * StringUtil.byteCountWithCharSet(null,        "UTF-8")           =  0
     * StringUtil.byteCountWithCharSet("",          "UTF-8")           =  0
     * StringUtil.byteCountWithCharSet("1",         "UTF-8")           =  1
     * StringUtil.byteCountWithCharSet("1中文",      "UTF-8")           =  7
     * 
     * StringUtil.byteCountWithCharSet(null,        "GBK")           =  0
     * StringUtil.byteCountWithCharSet("",          "GBK")           =  0
     * StringUtil.byteCountWithCharSet("1",         "GBK")           =  1
     * StringUtil.byteCountWithCharSet("1中文",      "GBK")           =  5
     * 
     * StringUtil.byteCountWithCharSet(null,        "ISO-8859-1")           =  0
     * StringUtil.byteCountWithCharSet("",          "ISO-8859-1")           =  0
     * StringUtil.byteCountWithCharSet("1",         "ISO-8859-1")           =  1
     * StringUtil.byteCountWithCharSet("1中文",      "ISO-8859-1")           =  3
     * </pre>
     * 
     * @param str
     *            待判断的字符串
     * @param charSetName
     *            编码字符集名称
     * @throws UnsupportedEncodingException
     *             输入的charSetName字符编码名称不支持
     * @return 字节数长度,null返回1
     * */
    public static int byteCountWithCharSet(String str, String charSetName) throws UnsupportedEncodingException {
        if (str == null) {
            return 0;
        }

        byte[] bytes = str.getBytes(charSetName);
        return bytes.length;
    }

    /**
     * 按照仅显示前六后四位的策略为卡号字符串中间部分打上*号<br>
     * 
     * <pre>
     * StringUtil.obfuscateCardNo(null)                    throws IllegalArgumentException<br>
     * StringUtil.obfuscateCardNo("")                      throws IllegalArgumentException<br>
     * StringUtil.obfuscateCardNo("12")                    throws IllegalArgumentException<br>
     * StringUtil.obfuscateCardNo("01234ab7890123*56")     throws IllegalArgumentException<br>
     * StringUtil.obfuscateCardNo("01234567890123456789")  throws IllegalArgumentException<br>
     * 
     * StringUtil.obfuscateCardNo("0123456789012345")    = "012345******2345"<br>
     * StringUtil.obfuscateCardNo("0123456789012345678") = "012345*********5678"<br>
     * </pre>
     * 
     * @param cardNo
     *            卡号字符串,16-19位数字
     * @throws IllegalArgumentException
     *             卡号为空，或者卡号格式不符
     * @return 中间用'*'隐藏的卡号字符串
     * */
    public static String obfuscateCardNo(String cardNo) {
        if (StringUtil.isEmpty(cardNo)) {
            throw new IllegalArgumentException("empty card no!");
        }

        if (!cardNoPattern.matcher(cardNo).matches()) {
            throw new IllegalArgumentException("invalid card no!");
        }

        // 前六显示
        StringBuilder sb = new StringBuilder();
        sb.append(cardNo.substring(0, 6));

        // 中间星号：星号长度=卡号长度 - 前六 - 后四
        int len = cardNo.length();
        int markLen = len - 6 - 4;
        sb.append(StringUtil.repeat('*', markLen));

        // 后四显示
        sb.append(cardNo.substring(len - 4));
        return sb.toString();
    }

}
